/*-------------------------------------------------------------------------
 *
 * parse_type.c
 *		处理解析器的类型操作
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_type.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "parser/parser.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"

static int32 fc_typenameTypeMod(ParseState *fc_pstate, const TypeName *fc_typeName,
							 Type fc_typ);


/* 
 * LookupTypeName
 *		典型情况的包装器。
 */
Type LookupTypeName(ParseState *fc_pstate, const TypeName *fc_typeName,
			   int32 *fc_typmod_p, bool fc_missing_ok)
{
	return LookupTypeNameExtended(fc_pstate,
								  fc_typeName, fc_typmod_p, true, fc_missing_ok);
}

/* 
 * LookupTypeNameExtended
 *		给定一个 TypeName 对象，查找该类型的 pg_type 系统缓存条目。
 *		如果找不到该类型，则返回 NULL。 如果找到该类型，
 *		则计算并存储在 TypeName 结构中表示的 typmod 值到 *typmod_p 中。
 *
 * 注意：成功时，调用者完成后必须释放类型元组的系统缓存。
 *
 * 注意：此函数的直接调用者必须在假设类型完全有效之前检查 typisdefined。 大多数代码应该使用 typenameType
 * 或 typenameTypeId。
 *
 * 如果调用者不关心 typmod 值，可以将 typmod_p 传递为 NULL，
 * 但无论如何会验证 typmod 装饰（如果有），除非找不到该类型。 注意，如果找到该类型但它是 shell，
 * 并且存在 typmod 装饰，将抛出错误——这是有意的。
 *
 * 如果 temp_ok 为 false，则忽略临时命名空间中的类型。 当调用者将根据适合度标准决定
 * typeName 实际上是类型还是其他东西时，传递 false。 如果 typeName 始终表示
 * 一个类型（或不表示任何东西），则传递 true。
 *
 * pstate 仅用于错误位置信息，可以为 NULL。
 */
Type LookupTypeNameExtended(ParseState *fc_pstate,
					   const TypeName *fc_typeName, int32 *fc_typmod_p,
					   bool fc_temp_ok, bool fc_missing_ok)
{
	Oid			fc_typoid;
	HeapTuple	fc_tup;
	int32		fc_typmod;

	if (fc_typeName->names == NIL)
	{
		/* 如果这是内部生成的 TypeName，则我们已经有 OID */
		fc_typoid = fc_typeName->typeOid;
	}
	else if (fc_typeName->pct_type)
	{
		/* 处理对现有字段类型的 %TYPE 引用 */
		RangeVar   *fc_rel = makeRangeVar(NULL, NULL, fc_typeName->location);
		char	   *fc_field = NULL;
		Oid			fc_relid;
		AttrNumber	fc_attnum;

		/* 解构名称列表 */
		switch (list_length(fc_typeName->names))
		{
			case 1:
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("improper %%TYPE reference (too few dotted names): %s",
								NameListToString(fc_typeName->names)),
						 parser_errposition(fc_pstate, fc_typeName->location)));
				break;
			case 2:
				fc_rel->relname = strVal(linitial(fc_typeName->names));
				fc_field = strVal(lsecond(fc_typeName->names));
				break;
			case 3:
				fc_rel->schemaname = strVal(linitial(fc_typeName->names));
				fc_rel->relname = strVal(lsecond(fc_typeName->names));
				fc_field = strVal(lthird(fc_typeName->names));
				break;
			case 4:
				fc_rel->catalogname = strVal(linitial(fc_typeName->names));
				fc_rel->schemaname = strVal(lsecond(fc_typeName->names));
				fc_rel->relname = strVal(lthird(fc_typeName->names));
				fc_field = strVal(lfourth(fc_typeName->names));
				break;
			default:
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("improper %%TYPE reference (too many dotted names): %s",
								NameListToString(fc_typeName->names)),
						 parser_errposition(fc_pstate, fc_typeName->location)));
				break;
		}

		/* 
		 * 查找字段。
		 *
		 * XXX：由于这里没有获取锁，因此在并发的 DDL 存在时可能会失败。
		 * 但是获取锁会带来性能损失，并且还需要进行权限检查。
		 */
		fc_relid = RangeVarGetRelid(fc_rel, NoLock, fc_missing_ok);
		fc_attnum = get_attnum(fc_relid, fc_field);
		if (fc_attnum == InvalidAttrNumber)
		{
			if (fc_missing_ok)
				fc_typoid = InvalidOid;
			else
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("column \"%s\" of relation \"%s\" does not exist",
								fc_field, fc_rel->relname),
						 parser_errposition(fc_pstate, fc_typeName->location)));
		}
		else
		{
			fc_typoid = get_atttype(fc_relid, fc_attnum);

			/* 此构造不应具有数组指示符 */
			Assert(fc_typeName->arrayBounds == NIL);

			/* 发出烦恼通知（故意没有 errposition'd） */
			ereport(NOTICE,
					(errmsg("type reference %s converted to %s",
							TypeNameToString(fc_typeName),
							format_type_be(fc_typoid))));
		}
	}
	else
	{
		/* 对类型名称的正常引用 */
		char	   *fc_schemaname;
		char	   *fc_typname;

		/* 解构名称列表 */
		DeconstructQualifiedName(fc_typeName->names, &fc_schemaname, &fc_typname);

		if (fc_schemaname)
		{
			/* 仅在特定模式中查找 */
			Oid			fc_namespaceId;
			ParseCallbackState fc_pcbstate;

			setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_typeName->location);

			fc_namespaceId = LookupExplicitNamespace(fc_schemaname, fc_missing_ok);
			if (OidIsValid(fc_namespaceId))
				fc_typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
										 PointerGetDatum(fc_typname),
										 ObjectIdGetDatum(fc_namespaceId));
			else
				fc_typoid = InvalidOid;

			cancel_parser_errposition_callback(&fc_pcbstate);
		}
		else
		{
			/* 非限定类型名称，因此搜索搜索路径 */
			fc_typoid = TypenameGetTypidExtended(fc_typname, fc_temp_ok);
		}

		/* 如果是数组引用，则返回数组类型 */
		if (fc_typeName->arrayBounds != NIL)
			fc_typoid = get_array_type(fc_typoid);
	}

	if (!OidIsValid(fc_typoid))
	{
		if (fc_typmod_p)
			*fc_typmod_p = -1;
		return NULL;
	}

	fc_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_typoid));
	if (!HeapTupleIsValid(fc_tup)) /* 不应该发生 */
		elog(ERROR, "cache lookup failed for type %u", fc_typoid);

	fc_typmod = fc_typenameTypeMod(fc_pstate, fc_typeName, (Type) fc_tup);

	if (fc_typmod_p)
		*fc_typmod_p = fc_typmod;

	return (Type) fc_tup;
}

/* 
 * LookupTypeNameOid
 *		给定一个 TypeName 对象，查找该类型的 pg_type 系统缓存条目。
 *		如果找不到该类型，则返回 InvalidOid。 如果找到该类型，
 *		返回其 Oid。
 *
 * 注意：此函数的直接调用者需要意识到返回的类型 OID
 * 可能对应于一个 shell 类型。 大多数代码应该使用
 * typenameTypeId。
 *
 * pstate 仅用于错误位置信息，可以为 NULL。
 */
Oid LookupTypeNameOid(ParseState *fc_pstate, const TypeName *fc_typeName, bool fc_missing_ok)
{
	Oid			fc_typoid;
	Type		fc_tup;

	fc_tup = LookupTypeName(fc_pstate, fc_typeName, NULL, fc_missing_ok);
	if (fc_tup == NULL)
	{
		if (!fc_missing_ok)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("type \"%s\" does not exist",
							TypeNameToString(fc_typeName)),
					 parser_errposition(fc_pstate, fc_typeName->location)));

		return InvalidOid;
	}

	fc_typoid = ((Form_pg_type) GETSTRUCT(fc_tup))->oid;
	ReleaseSysCache(fc_tup);

	return fc_typoid;
}

/* 
 * typenameType - 给定一个 TypeName，返回一个类型结构和 typmod
 *
 * 这与 LookupTypeName 等价，但如果找不到类型或类型未定义，
 * 将报告适当的错误消息。因此，该调用者可以假设结果是一个完全有效的类型。
 */
Type typenameType(ParseState *fc_pstate, const TypeName *fc_typeName, int32 *fc_typmod_p)
{
	Type		fc_tup;

	fc_tup = LookupTypeName(fc_pstate, fc_typeName, fc_typmod_p, false);
	if (fc_tup == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("type \"%s\" does not exist",
						TypeNameToString(fc_typeName)),
				 parser_errposition(fc_pstate, fc_typeName->location)));
	if (!((Form_pg_type) GETSTRUCT(fc_tup))->typisdefined)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("type \"%s\" is only a shell",
						TypeNameToString(fc_typeName)),
				 parser_errposition(fc_pstate, fc_typeName->location)));
	return fc_tup;
}

/* 
 * typenameTypeId - 给定一个 TypeName，返回类型的 OID
 *
 * 这类似于 typenameType，但我们只返回类型 OID
 * 而不是系统缓存条目。
 */
Oid typenameTypeId(ParseState *fc_pstate, const TypeName *fc_typeName)
{
	Oid			fc_typoid;
	Type		fc_tup;

	fc_tup = typenameType(fc_pstate, fc_typeName, NULL);
	fc_typoid = ((Form_pg_type) GETSTRUCT(fc_tup))->oid;
	ReleaseSysCache(fc_tup);

	return fc_typoid;
}

/* 
 * typenameTypeIdAndMod - 给定一个 TypeName，返回类型的 OID 和 typmod
 *
 * 这与 typenameType 等价，但我们只返回类型 OID
 * 和 typmod，而不是系统缓存条目。
 */
void typenameTypeIdAndMod(ParseState *fc_pstate, const TypeName *fc_typeName,
					 Oid *fc_typeid_p, int32 *fc_typmod_p)
{
	Type		fc_tup;

	fc_tup = typenameType(fc_pstate, fc_typeName, fc_typmod_p);
	*fc_typeid_p = ((Form_pg_type) GETSTRUCT(fc_tup))->oid;
	ReleaseSysCache(fc_tup);
}

/* 
 * typenameTypeMod - 给定一个 TypeName，返回内部 typmod 值
 *
 * 如果 TypeName 包含对数据类型非法的类型修饰符，将抛出错误。
 *
 * 实际类型 OID 由 TypeName 表示，必须已经被查找，并作为“typ”传递。
 *
 * pstate 仅用于错误位置信息，可以为 NULL。
 */
static int32 fc_typenameTypeMod(ParseState *fc_pstate, const TypeName *fc_typeName, Type fc_typ)
{
	int32		fc_result;
	Oid			fc_typmodin;
	Datum	   *fc_datums;
	int			fc_n;
	ListCell   *fc_l;
	ArrayType  *fc_arrtypmod;
	ParseCallbackState fc_pcbstate;

	/* 如果没有 typmod 表达式，则返回预设 typmod */
	if (fc_typeName->typmods == NIL)
		return fc_typeName->typemod;

	/* 
	 * 否则，类型最好接受 typmods。 我们针对 shell 类型案例给出特殊错误消息，
	 * 因为 shell 不可能具有 typmodin 函数。
	 */
	if (!((Form_pg_type) GETSTRUCT(fc_typ))->typisdefined)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("type modifier cannot be specified for shell type \"%s\"",
						TypeNameToString(fc_typeName)),
				 parser_errposition(fc_pstate, fc_typeName->location)));

	fc_typmodin = ((Form_pg_type) GETSTRUCT(fc_typ))->typmodin;

	if (fc_typmodin == InvalidOid)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("type modifier is not allowed for type \"%s\"",
						TypeNameToString(fc_typeName)),
				 parser_errposition(fc_pstate, fc_typeName->location)));

	/*
	 * 将原始语法输出表达式的列表转换为 cstring 数组。
	 * 目前，我们允许简单的数字常量、字符串文字和
	 * 标识符；可能这个列表可以扩展。
	 */
	fc_datums = (Datum *) palloc(list_length(fc_typeName->typmods) * sizeof(Datum));
	fc_n = 0;
	foreach(fc_l, fc_typeName->typmods)
	{
		Node	   *fc_tm = (Node *) lfirst(fc_l);
		char	   *fc_cstr = NULL;

		if (IsA(fc_tm, A_Const))
		{
			A_Const    *fc_ac = (A_Const *) fc_tm;

			if (IsA(&fc_ac->val, Integer))
			{
				fc_cstr = psprintf("%ld", (long) intVal(&fc_ac->val));
			}
			else if (IsA(&fc_ac->val, Float))
			{
				/* 我们可以直接使用字符串表示。 */
				fc_cstr = fc_ac->val.fval.fval;
			}
			else if (IsA(&fc_ac->val, String))
			{
				/* 我们可以直接使用字符串表示。 */
				fc_cstr = strVal(&fc_ac->val);
			}
		}
		else if (IsA(fc_tm, ColumnRef))
		{
			ColumnRef  *fc_cr = (ColumnRef *) fc_tm;

			if (list_length(fc_cr->fields) == 1 &&
				IsA(linitial(fc_cr->fields), String))
				fc_cstr = strVal(linitial(fc_cr->fields));
		}
		if (!fc_cstr)
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("type modifiers must be simple constants or identifiers"),
					 parser_errposition(fc_pstate, fc_typeName->location)));
		fc_datums[fc_n++] = CStringGetDatum(fc_cstr);
	}

	/* 关于 cstring 表示细节的硬编码知识在这里 */
	fc_arrtypmod = construct_array(fc_datums, fc_n, CSTRINGOID,
								-2, false, TYPALIGN_CHAR);

	/* 如果类型的 typmodin 函数失败，则安排报告位置 */
	setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_typeName->location);

	fc_result = DatumGetInt32(OidFunctionCall1(fc_typmodin,
											PointerGetDatum(fc_arrtypmod)));

	cancel_parser_errposition_callback(&fc_pcbstate);

	pfree(fc_datums);
	pfree(fc_arrtypmod);

	return fc_result;
}

/*
 * appendTypeNameToBuffer
 *		将表示 TypeName 名称的字符串附加到 StringInfo。
 *		这是 TypeNameToString 和 TypeNameListToString 的共享核心部分。
 *
 * 注意：这必须适用于不描述任何实际类型的 TypeName；
 * 它主要用于报告查找错误。
 */
static void fc_appendTypeNameToBuffer(const TypeName *fc_typeName, StringInfo fc_string)
{
	if (fc_typeName->names != NIL)
	{
		/* 按原样发出可能的合格名称 */
		ListCell   *fc_l;

		foreach(fc_l, fc_typeName->names)
		{
			if (fc_l != list_head(fc_typeName->names))
				appendStringInfoChar(fc_string, '.');
			appendStringInfoString(fc_string, strVal(lfirst(fc_l)));
		}
	}
	else
	{
		/* 查找内部指定的类型 */
		appendStringInfoString(fc_string, format_type_be(fc_typeName->typeOid));
	}

	/*
	 * 根据需要添加装饰，但仅针对 LookupTypeName 考虑的字段
	 */
	if (fc_typeName->pct_type)
		appendStringInfoString(fc_string, "%TYPE");

	if (fc_typeName->arrayBounds != NIL)
		appendStringInfoString(fc_string, "[]");
}

/*
 * TypeNameToString
 *		生成一个表示 TypeName 名称的字符串。
 *
 * 注意：这必须适用于不描述任何实际类型的 TypeName；
 * 它主要用于报告查找错误。
 */
char * TypeNameToString(const TypeName *fc_typeName)
{
	StringInfoData fc_string;

	initStringInfo(&fc_string);
	fc_appendTypeNameToBuffer(fc_typeName, &fc_string);
	return fc_string.data;
}

/*
 * TypeNameListToString
 *		生成一个表示 TypeName 列表名称的字符串
 */
char * TypeNameListToString(List *fc_typenames)
{
	StringInfoData fc_string;
	ListCell   *fc_l;

	initStringInfo(&fc_string);
	foreach(fc_l, fc_typenames)
	{
		TypeName   *fc_typeName = lfirst_node(TypeName, fc_l);

		if (fc_l != list_head(fc_typenames))
			appendStringInfoChar(&fc_string, ',');
		fc_appendTypeNameToBuffer(fc_typeName, &fc_string);
	}
	return fc_string.data;
}

/*
 * LookupCollation
 *
 * 按名称查找排序规则，返回 OID，并支持错误位置。
 */
Oid LookupCollation(ParseState *fc_pstate, List *fc_collnames, int fc_location)
{
	Oid			fc_colloid;
	ParseCallbackState fc_pcbstate;

	if (fc_pstate)
		setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_location);

	fc_colloid = get_collation_oid(fc_collnames, false);

	if (fc_pstate)
		cancel_parser_errposition_callback(&fc_pcbstate);

	return fc_colloid;
}

/*
 * GetColumnDefCollation
 *
 * 获取在定义列时使用的排序规则，给定 ColumnDef 节点和先前确定的列类型 OID。
 *
 * pstate 仅用于错误位置目的，可以为 NULL。
 */
Oid GetColumnDefCollation(ParseState *fc_pstate, ColumnDef *fc_coldef, Oid fc_typeOid)
{
	Oid			fc_result;
	Oid			fc_typcollation = get_typcollation(fc_typeOid);
	int			fc_location = fc_coldef->location;

	if (fc_coldef->collClause)
	{
		/* 我们有一个原始的 COLLATE 子句，因此查找排序规则 */
		fc_location = fc_coldef->collClause->location;
		fc_result = LookupCollation(fc_pstate, fc_coldef->collClause->collname,
								 fc_location);
	}
	else if (OidIsValid(fc_coldef->collOid))
	{
		/* 预先处理的排序规则规范，使用它 */
		fc_result = fc_coldef->collOid;
	}
	else
	{
		/* 如果有，则使用类型的默认排序规则 */
		fc_result = fc_typcollation;
	}

	/* 如果 COLLATE 应用于不可排序类型，则进行抱怨 */
	if (OidIsValid(fc_result) && !OidIsValid(fc_typcollation))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("collations are not supported by type %s",
						format_type_be(fc_typeOid)),
				 parser_errposition(fc_pstate, fc_location)));

	return fc_result;
}

/* 根据类型 ID 返回一个类型结构 */
/* 注意：调用者在完成后必须释放类型元组的系统缓存 */
Type typeidType(Oid fc_id)
{
	HeapTuple	fc_tup;

	fc_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_id));
	if (!HeapTupleIsValid(fc_tup))
		elog(ERROR, "cache lookup failed for type %u", fc_id);
	return (Type) fc_tup;
}

/* 给定类型（作为类型结构），返回类型 OID */
Oid typeTypeId(Type fc_tp)
{
	if (fc_tp == NULL)				/* 可能无用 */
		elog(ERROR, "typeTypeId() called with NULL type struct");
	return ((Form_pg_type) GETSTRUCT(fc_tp))->oid;
}

/* 给定类型（作为类型结构），返回类型的长度 */
int16 typeLen(Type fc_t)
{
	Form_pg_type fc_typ;

	fc_typ = (Form_pg_type) GETSTRUCT(fc_t);
	return fc_typ->typlen;
}

/* 给定类型（作为类型结构），返回其 'byval' 属性 */
bool typeByVal(Type fc_t)
{
	Form_pg_type fc_typ;

	fc_typ = (Form_pg_type) GETSTRUCT(fc_t);
	return fc_typ->typbyval;
}

/* 给定类型（作为类型结构），返回类型的名称 */
char * typeTypeName(Type fc_t)
{
	Form_pg_type fc_typ;

	fc_typ = (Form_pg_type) GETSTRUCT(fc_t);
	/* 这里使用 pstrdup，因为结果可能需要超出系统缓存条目的生命周期 */
	return pstrdup(NameStr(fc_typ->typname));
}

/* 给定类型（作为类型结构），返回其 'typrelid' 属性 */
Oid typeTypeRelid(Type fc_typ)
{
	Form_pg_type fc_typtup;

	fc_typtup = (Form_pg_type) GETSTRUCT(fc_typ);
	return fc_typtup->typrelid;
}

/* 给定类型（作为类型结构），返回其 'typcollation' 属性 */
Oid typeTypeCollation(Type fc_typ)
{
	Form_pg_type fc_typtup;

	fc_typtup = (Form_pg_type) GETSTRUCT(fc_typ);
	return fc_typtup->typcollation;
}

/*
 * 给定类型结构和字符串，返回该字符串的内部表示。
 * “字符串”可以为 NULL 以执行 NULL 的转换
 * （如果输入函数拒绝 NULL，可能会导致失败）。
 */
Datum stringTypeDatum(Type fc_tp, char *fc_string, int32 fc_atttypmod)
{
	Form_pg_type fc_typform = (Form_pg_type) GETSTRUCT(fc_tp);
	Oid			fc_typinput = fc_typform->typinput;
	Oid			fc_typioparam = getTypeIOParam(fc_tp);

	return OidInputFunctionCall(fc_typinput, fc_string, fc_typioparam, fc_atttypmod);
}

/*
 * 给定类型 ID，返回该类型的 typrelid（关联关系），如果有的话。
 * 如果类型不是复合类型，则返回 InvalidOid。
 */
Oid typeidTypeRelid(Oid fc_type_id)
{
	HeapTuple	fc_typeTuple;
	Form_pg_type fc_type;
	Oid			fc_result;

	fc_typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_type_id));
	if (!HeapTupleIsValid(fc_typeTuple))
		elog(ERROR, "cache lookup failed for type %u", fc_type_id);
	fc_type = (Form_pg_type) GETSTRUCT(fc_typeTuple);
	fc_result = fc_type->typrelid;
	ReleaseSysCache(fc_typeTuple);
	return fc_result;
}

/*
 * 给定类型 ID，返回该类型的 typrelid（关联关系），如果有的话。
 * 如果类型不是复合类型或其上的域，则返回 InvalidOid。
 * 这与 typeidTypeRelid(getBaseType(type_id)) 相同，但更快。
 */
Oid typeOrDomainTypeRelid(Oid fc_type_id)
{
	HeapTuple	fc_typeTuple;
	Form_pg_type fc_type;
	Oid			fc_result;

	for (;;)
	{
		fc_typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_type_id));
		if (!HeapTupleIsValid(fc_typeTuple))
			elog(ERROR, "cache lookup failed for type %u", fc_type_id);
		fc_type = (Form_pg_type) GETSTRUCT(fc_typeTuple);
		if (fc_type->typtype != TYPTYPE_DOMAIN)
		{
			/* 不是一个域，因此停止查找域 */
			break;
		}
		/* 它是一个域，因此检查基础类型 */
		fc_type_id = fc_type->typbasetype;
		ReleaseSysCache(fc_typeTuple);
	}
	fc_result = fc_type->typrelid;
	ReleaseSysCache(fc_typeTuple);
	return fc_result;
}

/*
 * 在 parseTypeString() 期间解析失败的错误上下文回调
 */
static void fc_pts_error_callback(void *fc_arg)
{
	const char *fc_str = (const char *) fc_arg;

	errcontext("invalid type name \"%s\"", fc_str);
}

/*
 * 给定一个应该是 SQL 兼容的类型声明字符串，
 * 例如 "int4" 或 "integer" 或 "character varying(32)"，解析
 * 字符串并将结果作为 TypeName 返回。
 * 如果字符串无法解析为类型，将抛出错误。
 */
TypeName * typeStringToTypeName(const char *fc_str)
{
	List	   *fc_raw_parsetree_list;
	TypeName   *fc_typeName;
	ErrorContextCallback fc_ptserrcontext;

	/* 确保我们为空输入提供有用的错误信息 */
	if (strspn(fc_str, " \t\n\r\f") == strlen(fc_str))
		goto fail;

	/*
	 * 设置错误追踪支持，以便在解析期间调用 ereport()
	 */
	fc_ptserrcontext.callback = fc_pts_error_callback;
	fc_ptserrcontext.arg = unconstify(char *, fc_str);
	fc_ptserrcontext.previous = error_context_stack;
	error_context_stack = &fc_ptserrcontext;

	fc_raw_parsetree_list = raw_parser(fc_str, RAW_PARSE_TYPE_NAME);

	error_context_stack = fc_ptserrcontext.previous;

	/* 我们应该得到一个 TypeName 节点。 */
	Assert(list_length(fc_raw_parsetree_list) == 1);
	fc_typeName = linitial_node(TypeName, fc_raw_parsetree_list);

	/* 语法允许在 TypeName 中使用 SETOF，但我们在这里不想要。 */
	if (fc_typeName->setof)
		goto fail;

	return fc_typeName;

fail:
	ereport(ERROR,
			(errcode(ERRCODE_SYNTAX_ERROR),
			 errmsg("invalid type name \"%s\"", fc_str)));
	return NULL;				/* 保持编译器安静 */
}

/*
 * 给定一个应该是 SQL 兼容的类型声明字符串，
 * 例如 "int4" 或 "integer" 或 "character varying(32)"，解析
 * 字符串并将其转换为类型 OID 和类型修饰符。
 * 如果 missing_ok 为 true，则在未找到类型名称时返回 InvalidOid，而不是抛出错误。
 */
void parseTypeString(const char *fc_str, Oid *fc_typeid_p, int32 *fc_typmod_p, bool fc_missing_ok)
{
	TypeName   *fc_typeName;
	Type		fc_tup;

	fc_typeName = typeStringToTypeName(fc_str);

	fc_tup = LookupTypeName(NULL, fc_typeName, fc_typmod_p, fc_missing_ok);
	if (fc_tup == NULL)
	{
		if (!fc_missing_ok)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("type \"%s\" does not exist",
							TypeNameToString(fc_typeName)),
					 parser_errposition(NULL, fc_typeName->location)));
		*fc_typeid_p = InvalidOid;
	}
	else
	{
		Form_pg_type fc_typ = (Form_pg_type) GETSTRUCT(fc_tup);

		if (!fc_typ->typisdefined)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("type \"%s\" is only a shell",
							TypeNameToString(fc_typeName)),
					 parser_errposition(NULL, fc_typeName->location)));
		*fc_typeid_p = fc_typ->oid;
		ReleaseSysCache(fc_tup);
	}
}
